
#ifndef __LIBERTY_ENGINE_IOBJECTMETHOD_H__
#define __LIBERTY_ENGINE_IOBJECTMETHOD_H__

#include <vector>
#include <boost/any.hpp>

namespace Liberty { namespace Engine {
    class IObjectMethod {
    public:
        virtual ~IObjectMethod() {}
        
    public:
        virtual boost::any call(const std::vector<boost::any>&) = 0;
    };

    template <typename RT, typename T>
    class ObjectMethod : public IObjectMethod {
    public:
        typedef RT (T::*member_type)(void);

    protected:
        std::shared_ptr<T> _object;
        member_type        _member;

    public:
        ObjectMethod(const std::shared_ptr<T>& object, member_type member) : _object(object), _member(member) {
        }

        virtual boost::any call(const std::vector<boost::any>&) {
            return ((*(_object.get())).*_member)();
        }

        static std::shared_ptr<IObjectMethod> alloc(const std::shared_ptr<T>& object, member_type member) {
            return std::shared_ptr<IObjectMethod>(new ObjectMethod<RT, T>(object, member));
        }
    };

    template <typename T>
    class ObjectMethod<void, T> : public IObjectMethod {
    public:
        typedef void (T::*member_type)(void);

    protected:
        std::shared_ptr<T> _object;
        member_type        _member;

    public:
        ObjectMethod(const std::shared_ptr<T>& object, member_type member) : _object(object), _member(member) {
        }

        virtual boost::any call(const std::vector<boost::any>&) {
            ((*(_object.get())).*_member)();
            return boost::any();
        }

        static std::shared_ptr<IObjectMethod> alloc(const std::shared_ptr<T>& object, member_type member) {
            return std::shared_ptr<IObjectMethod>(new ObjectMethod<void, T>(object, member));
        }
    };

    template <typename RT, typename T, typename A1, typename A1D = A1>
    class ObjectMethod1 : public IObjectMethod {
    public:
        typedef RT (T::*member_type)(A1D);

    protected:
        std::shared_ptr<T> _object;
        member_type        _member;

    public:
        ObjectMethod1(const std::shared_ptr<T>& object, member_type member) : _object(object), _member(member) {
        }

        virtual boost::any call(const std::vector<boost::any>& v) {
            if (v.size() < 1) {
                throw std::runtime_error("Missing argument");
            }

            const A1* a1 = boost::any_cast<A1>(&(v[0]));

            return ((*(_object.get())).*_member)(*a1);
        }

        static std::shared_ptr<IObjectMethod> alloc(const std::shared_ptr<T>& object, member_type member) {
            return std::shared_ptr<IObjectMethod>(new ObjectMethod1<RT, T, A1, A1D>(object, member));
        }
    };

    template <typename T, typename A1, typename A1D>
    class ObjectMethod1<void, T, A1, A1D> : public IObjectMethod {
    public:
        typedef void (T::*member_type)(A1D);

    protected:
        std::shared_ptr<T> _object;
        member_type        _member;

    public:
        ObjectMethod1(const std::shared_ptr<T>& object, member_type member) : _object(object), _member(member) {
        }

        virtual boost::any call(const std::vector<boost::any>& v) {
            if (v.size() < 1) {
                throw std::runtime_error("Missing argument");
            }

            const A1* a1 = boost::any_cast<A1>(&(v[0]));

            ((*(_object.get())).*_member)(*a1);
            return boost::any();
        }
        
        static std::shared_ptr<IObjectMethod> alloc(const std::shared_ptr<T>& object, member_type member) {
            return std::shared_ptr<IObjectMethod>(new ObjectMethod1<void, T, A1, A1D>(object, member));
        }
    };
}}

#endif
